package com.ymatou.shop.ui.view;

import java.util.Date;

import android.content.Context;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.GestureDetector;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.Scroller;
import android.widget.TextView;

import com.ymatou.shop.R;
import com.ymatou.shop.util.DateUtil;


public class PullToRefreshView extends FrameLayout implements
		GestureDetector.OnGestureListener,
		Animation.AnimationListener {
	
	/**
     * Interface definition for a callback to be invoked when list should be
     * refreshed.
     */
    public interface OnRefreshListener {
        /**
         * Called when the list should be refreshed.
         * <p>
         * A call to {@link PullToRefreshListView #onRefreshComplete()} is
         * expected to indicate that the refresh has completed.
         */
        public void onRefresh();
    }
	
	private class Flinger implements Runnable {

		private int mLastFlingX;
		private Scroller mScroller;

		private void startCommon() {
			removeCallbacks(this);
		}

		public void run() {
			boolean isRun = mScroller.computeScrollOffset();
			int currX = mScroller.getCurrX();
			int distance = mLastFlingX - currX;
			PullToRefreshView.this.move(distance, false);
			updateView();
			if (isRun) {
				mLastFlingX = currX;
				post(this);
			} else {
				mIsAutoScroller = false;
				removeCallbacks(this);
			}
		}

		public void startUsingDistance(int distance, int duration) {
			if (distance == 0) {
				distance--;
			}
			startCommon();
			mLastFlingX = 0;
			mScroller.startScroll(0, 0, -distance, 0, duration);
			mIsAutoScroller = true;
			post(this);
		}

		public Flinger() {
			Context context = getContext();
			mScroller = new Scroller(context);
		}
	}

	public static int MAX_LENGHT = 0;
	public static final int STATE_CLOSE = 1;
	public static final int STATE_OPEN = 2;
	public static final int STATE_OPEN_RELEASE = 3;
	public static final int STATE_OPEN_MAX = 4;
	public static final int STATE_OPEN_MAX_RELEASE = 5;
	public static final int STATE_UPDATE = 6;
	public static final int STATE_UPDATE_SCROLL = 7;
	private View child;
	private Animation mAnimationDown;
	private Animation mAnimationUp;
	private ImageView mArrow;
	private String mlastUpdated;
	private GestureDetector mDetector;
	private Drawable mDownArrow;
	private boolean mEnable;
	private Flinger mFlinger;
	private boolean mIsAutoScroller;
	private int mPading;
	private ProgressBar mProgressBar;
	private int mState;
	private TextView mTitle;
	private TextView mDate;
	private Drawable mUpArrow;
	private FrameLayout mUpdateContent;
	private OnRefreshListener mOnRefreshListener;

	public PullToRefreshView(Context context) {
		super(context);
		init(context);
	}

	public PullToRefreshView(Context context, AttributeSet attributeset) {
		super(context, attributeset);
		init(context);
	}
	
	public PullToRefreshView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init(context);
    }

	private void addUpdateBar() {
		mAnimationUp = AnimationUtils.loadAnimation(getContext(),
				R.anim.rotate_up);
		mAnimationUp.setAnimationListener(this);
		mAnimationDown = AnimationUtils.loadAnimation(getContext(),
				R.anim.rotate_down);
		mAnimationDown.setAnimationListener(this);
		child = LayoutInflater.from(getContext()).inflate(
				R.layout.vw_update_bar, null);
		child.setVisibility(View.INVISIBLE);
		addView(child);
		
		Context context = getContext();
		
		mUpdateContent = (FrameLayout) getChildAt(0).findViewById(R.id.iv_content);
		
		mArrow = new ImageView(context);
		mArrow.setScaleType(ImageView.ScaleType.FIT_CENTER);
		mArrow.setLayoutParams(new FrameLayout.LayoutParams(
				FrameLayout.LayoutParams.FILL_PARENT,
				FrameLayout.LayoutParams.FILL_PARENT));
		mArrow.setImageResource(R.drawable.z_arrow_down);
		mUpdateContent.addView(mArrow);

		FrameLayout.LayoutParams progressBarLayoutParams = new FrameLayout.LayoutParams(
				FrameLayout.LayoutParams.FILL_PARENT,
				FrameLayout.LayoutParams.FILL_PARENT);
		progressBarLayoutParams.gravity = Gravity.CENTER;
		mProgressBar = new ProgressBar(context, null,
				android.R.attr.progressBarStyleInverse);
		int padding = getResources().getDimensionPixelSize(
				R.dimen.updatebar_padding);
		mProgressBar.setPadding(padding, padding, padding, padding);
		mProgressBar.setLayoutParams(progressBarLayoutParams);
		mUpdateContent.addView(mProgressBar);
		
		mTitle = (TextView) findViewById(R.id.tv_title);
		mDate = (TextView) findViewById(R.id.tv_date);
	}

	private void init(Context context) {
		mDetector = new GestureDetector(this);
		mFlinger = new Flinger();
		mState = 1;
		mEnable = true;
		
		MAX_LENGHT = getResources().getDimensionPixelSize(
				R.dimen.updatebar_height);
		setDrawingCacheEnabled(false);
		setBackgroundDrawable(null);
		setClipChildren(false);
		mDetector.setIsLongpressEnabled(true);
		mUpArrow = context.getResources().getDrawable(R.drawable.z_arrow_up);
		mDownArrow = context.getResources().getDrawable(R.drawable.z_arrow_down);
		
		addUpdateBar();
	}

	private boolean move(float distance, boolean isScroll) {
		boolean consumed = false;
		if(mState == STATE_UPDATE) {
			if (distance >= 0F && isScroll) {
	    	    mState = STATE_UPDATE_SCROLL;
	    	    consumed = false;
	    	} else {
	    		consumed = false;
	    	}
		} else {
	    	if (mState == STATE_UPDATE_SCROLL && distance < 0F) {
		        if (-mPading >= MAX_LENGHT) {
		        	return true;
		        }
		    }
	    }
		
		mPading = (int) (mPading + distance);
	    if (mPading > 0) {
	        mPading = 0;
	    }
	    
	    if (isScroll) {
	    	switch(mState) {
	    	case STATE_CLOSE:
	    		if (mPading < 0) {
	    	        mState = STATE_OPEN;
	    	        mProgressBar.setVisibility(View.INVISIBLE);
	    	        mArrow.setVisibility(View.VISIBLE);
	    	    }
	    		break;
	    	case STATE_OPEN:
	    	    if (Math.abs(mPading) >= MAX_LENGHT) {
	    	        mState = STATE_OPEN_MAX;
	    	        mProgressBar.setVisibility(View.INVISIBLE);
	    	        mArrow.setVisibility(View.VISIBLE);
	    	        mArrow.startAnimation(mAnimationUp);
				} else if (mPading == 0) {
					mState = STATE_CLOSE;
				}
	    		break;
	    	case STATE_OPEN_RELEASE:
	    	case STATE_OPEN_MAX_RELEASE:
	    		if (isScroll) {
	    	        if (Math.abs(mPading) >= MAX_LENGHT) {
	    	            mState = STATE_OPEN_MAX;
	    	            mProgressBar.setVisibility(View.GONE);
	    	            mArrow.setVisibility(View.VISIBLE);
	    	            mArrow.startAnimation(mAnimationUp);
	    	        } else {
	    	            if (Math.abs(mPading) < MAX_LENGHT) {
	    	                mState = STATE_OPEN;
	    	                mProgressBar.setVisibility(View.INVISIBLE);
	    	                mArrow.setVisibility(View.VISIBLE);
	    	                mArrow.startAnimation(mAnimationDown);
						} else if (mPading == 0) {
							mState = STATE_CLOSE;
						}
	    	        }
	    	    } else if (mPading == 0) {
	    	        mState = STATE_CLOSE;
	    	    }
	    	    invalidate();
	    	    consumed = true;
	    		break;
	    	case STATE_OPEN_MAX:
	    	    if (Math.abs(mPading) < MAX_LENGHT) {
	    	        mState = STATE_OPEN;
	    	        mProgressBar.setVisibility(View.INVISIBLE);
	    	        mArrow.setVisibility(View.VISIBLE);
	    	        mArrow.startAnimation(mAnimationDown);
	    	    }
	    		break;
	    	case STATE_UPDATE:
	    		if (mPading == 0) {
	    	        mState = STATE_CLOSE;
	    	    }
	    	    invalidate();
	    	    consumed = true;
	    		break;
	    	}
	    } else {
	    	if (mState != STATE_OPEN_MAX_RELEASE) {
				if (mState == STATE_UPDATE && mPading == 0) {
					mState = STATE_CLOSE;
				} else if (mState == STATE_OPEN_RELEASE && mPading == 0) {
					mState = STATE_CLOSE;
				} else if (mState == STATE_UPDATE_SCROLL && mPading == 0) {
					mState = STATE_CLOSE;
				}
				invalidate();
				consumed = true;
			} else {
				mState = STATE_UPDATE;
				if (mOnRefreshListener != null) {
					mOnRefreshListener.onRefresh();
				}
			}
	    }
	    
		return consumed;
	}

	private boolean release() {
		if (mPading >= 0)
			return false;
		
		switch (mState) {
		case STATE_OPEN:
		case STATE_OPEN_RELEASE:
			int length = Math.abs(mPading);
			if (length < MAX_LENGHT) {
				mState = STATE_OPEN_RELEASE;
			}
			scrollToClose();
			break;
		case STATE_OPEN_MAX:
		case STATE_OPEN_MAX_RELEASE:
			mState = STATE_OPEN_MAX_RELEASE;
			scrollToUpdate();
			break;
		}
		return true;
	}

	public void scrollToClose() {
		mFlinger.startUsingDistance(-mPading, 300);
	}

	public void scrollToUpdate() {
		mFlinger.startUsingDistance((-mPading) - MAX_LENGHT, 300);
	}

	private void updateView() {
	    View updatebarView = getChildAt(0);
	    View view1 = getChildAt(1);
	    if (mlastUpdated == null) {
	    	mlastUpdated = "";
	    }
	    
	    switch(mState) {
	    case STATE_CLOSE:
	    	if (updatebarView.getVisibility() != View.INVISIBLE) {
	    		updatebarView.setVisibility(View.INVISIBLE);
		    }
		    view1.offsetTopAndBottom(-view1.getTop());
	    	break;
	    case STATE_OPEN: // continue
	    case STATE_OPEN_RELEASE:
		    view1.offsetTopAndBottom(-mPading - view1.getTop());
		    if (updatebarView.getVisibility() != View.VISIBLE) {
		    	updatebarView.setVisibility(View.VISIBLE);
		    }
		    updatebarView.offsetTopAndBottom((-MAX_LENGHT) - mPading - updatebarView.getTop());
		    mTitle.setText(getResources().getString(R.string.pull_to_refresh_pull_label));
		    mDate.setText(mlastUpdated);
	    	break;
	    case STATE_OPEN_MAX:
	    case STATE_OPEN_MAX_RELEASE:
		    view1.offsetTopAndBottom(-mPading - view1.getTop());
		    if (updatebarView.getVisibility() != View.VISIBLE) {
		    	updatebarView.setVisibility(View.VISIBLE);
		    }
		    updatebarView.offsetTopAndBottom((-MAX_LENGHT) - mPading - updatebarView.getTop());
		    mTitle.setText(getResources().getString(R.string.pull_to_refresh_release_label));
		    mDate.setText(mlastUpdated);
	    	break;
	    case STATE_UPDATE:
	    case STATE_UPDATE_SCROLL:
			view1.offsetTopAndBottom(-mPading - view1.getTop());
			if (mProgressBar.getVisibility() != View.VISIBLE) {
				mProgressBar.setVisibility(View.VISIBLE);
				mEnable = false;
			}
			if (mArrow.getVisibility() != View.INVISIBLE) {
				mArrow.setVisibility(View.INVISIBLE);
			}
		    
		    mTitle.setText(getResources().getString(R.string.pull_to_refresh_refreshing_label));
		    mDate.setText(mlastUpdated);
		    updatebarView.offsetTopAndBottom((-MAX_LENGHT) - mPading - updatebarView.getTop());
		    if (updatebarView.getVisibility() != View.VISIBLE) {
		    	updatebarView.setVisibility(View.VISIBLE);
		    }
	    	break;
	    }
	    
	    invalidate();
	}

	public boolean dispatchTouchEvent(MotionEvent me) {
		if (!mEnable)
			return super.dispatchTouchEvent(me);
	
		if(mIsAutoScroller)
			return true;

		boolean consumed = mDetector.onTouchEvent(me);
		int action = me.getAction();
		if (action == MotionEvent.ACTION_UP) {
			consumed = release();
		} else {
			if (action == MotionEvent.ACTION_CANCEL) {
				consumed = release();
			}
		}
		
		if (mState == STATE_UPDATE || mState == STATE_UPDATE_SCROLL) {
			updateView();
		} else if ((consumed 
				|| mState == STATE_OPEN
				|| mState == STATE_OPEN_MAX
				|| mState == STATE_OPEN_MAX_RELEASE 
				|| mState == STATE_OPEN_RELEASE) && getChildAt(1).getTop() != 0) {
			me.setAction(MotionEvent.ACTION_CANCEL);
	        super.dispatchTouchEvent(me);
	        updateView();
	        return true;
	    } else {
	        updateView();
	    }

		return super.dispatchTouchEvent(me);
	}
	
	/**
	 * Set a text to represent when the list was last updated.
	 * 
	 * @param lastUpdated
	 *            Last updated at.
	 */
	public void setLastUpdated(String lastUpdated) {
		mlastUpdated = lastUpdated;
	}
	
	public void setLastUpdated(Date date) {
		mlastUpdated = "最后加载更新：今天" + DateUtil.format(date, "HH:mm");
	}
	
	public void onRefreshComplete(Date date) {
		setLastUpdated("最后加载更新：今天" + DateUtil.format(date, "HH:mm"));
		onRefreshComplete();
	}
	
	/**
	 * Resets the list to a normal state after a refresh.
	 * 
	 * @param lastUpdated
	 *            Last updated at.
	 */
	public void onRefreshComplete(String lastUpdated) {
		setLastUpdated(lastUpdated);
		onRefreshComplete();
	}

    /**
     * Resets the list to a normal state after a refresh.
     */
    public void onRefreshComplete() {
    	((ListView) getChildAt(1)).invalidateViews();
    	mEnable = true;
    	
    	if (mPading != 0) {
			scrollToClose();
		} else {
			mState = STATE_CLOSE;
		}
    }

	public void onAnimationEnd(Animation animation) {
		if (mState == STATE_OPEN || mState == STATE_OPEN_RELEASE) {
			mArrow.setImageDrawable(mDownArrow);
			return;
		} else {
			mArrow.setImageDrawable(mUpArrow);
			return;
		}
	}

	@Override
	public void onAnimationRepeat(Animation animation) {
	}

	@Override
	public void onAnimationStart(Animation animation) {
	}

	@Override
	public boolean onDown(MotionEvent e) {
		return false;
	}

	@Override
	public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
			float velocityY) {
		return false;
	}
	
	@Override
	protected void onLayout(boolean changed, int left, int top, int right,
			int bottom) {
		View updatebarView = getChildAt(0);
		updatebarView.layout(0, (-MAX_LENGHT) - mPading, getMeasuredWidth(), -mPading);
		View listView = getChildAt(1);
		if (listView != null){
			listView.layout(0, -mPading, getMeasuredWidth(), getMeasuredHeight() - mPading);	
		}
		
	}

	@Override
	public void onLongPress(MotionEvent e) {
	}

	@Override
	public boolean onScroll(MotionEvent e1, MotionEvent e2,
			float distanceX, float distanceY) {
		
		boolean consumed;
		
		float distance = (float) ((double) distanceY * 0.5D);
	    ListView listView = (ListView) getChildAt(1);

	    if (listView == null 
				|| listView.getCount() == 0
				|| listView.getChildCount() == 0) {
			consumed = false;
	    } else {
	        boolean isFirst;
			if (listView.getFirstVisiblePosition() == 0
					&& listView.getChildAt(0).getTop() == 0) {
				isFirst = true;
			} else {
				isFirst = false;
			}

	        if (distance < 0F && isFirst || mPading < 0) {
	        	consumed = move(distance, true);
	        } else {
	        	consumed = false;
	        }
	    }
	    return consumed;
	}

	@Override
	public void onShowPress(MotionEvent e) {
	}

	@Override
	public boolean onSingleTapUp(MotionEvent e) {
		return false;
	}

	public void setPullToRefreshEnable(boolean enable) {
		mEnable = enable;
		invalidate();
	}

	/**
     * Register a callback to be invoked when this list should be refreshed.
     * 
     * @param onRefreshListener The callback to run.
     */
    public void setOnRefreshListener(OnRefreshListener onRefreshListener) {
        mOnRefreshListener = onRefreshListener;
    }

	public void updateWithoutOffset() {
		mState = STATE_UPDATE_SCROLL;
		invalidate();
	}
	
	
	public void setTitleColor(int color) {
		mTitle.setTextColor(color);
	}
	
	public boolean isRefresh() {
		return mState == STATE_UPDATE;
	}
}

